import { EventEmitter } from "events";
import { IPos } from "../../../common/vec2/Vec2";
import { MapEntity } from "./MapEntity";

/**
 * 灯塔AOI
 */
export class TowerAOI extends EventEmitter {

    /** 总宽度 */
    private _width: number;
    /** 总高度 */
    private _height: number;
    /** 灯塔宽度 */
    private _towerWidth: number;
    /** 灯塔高度 */
    private _towerHeight: number;
    /** x方向灯塔数量 */
    private _towerX: number;
    /** y方向灯塔数量 */
    private _towerY: number;
    /** 所有灯塔 */
    private _towers: Tower[][];

    constructor(width: number, height: number, towerWitdh: number, towerHeight: number) {
        super();
        this._width = width;
        this._height = height;
        this._towerWidth = towerWitdh;
        this._towerHeight = towerHeight;

        this._towers = [];
        this._towerX = Math.ceil(width / towerWitdh);
        this._towerY = Math.ceil(height / towerHeight);
        for (let y = 0; y < this._towerY; ++y) {
            this._towers[y] = [];
            for (let x = 0; x < this._towerX; ++x) {
                this._towers[y][x] = new Tower();
            }
        }
    }

    /**
     * 监听灯塔事件
     * @param id 
     * @param listener 
     * @returns 
     */
    on(eventId: TowerEventId, listener: (watchers: MapEntity[], entities: MapEntity[]) => void) {
        return super.on(eventId, listener);
    }

    /**
     * 发送灯塔事件
     * @param eventId 
     * @param watchers
     * @param entities 
     * @returns 
     */
    emit(eventId: TowerEventId, watchers: MapEntity[], entities: MapEntity[]) {
        return super.emit(eventId, watchers, entities);
    }

    /**
     * 添加实体
     * @param entity 
     */
    addEntity(entity: MapEntity, tower?: Tower) {
        this.delEntity(entity);
        tower ||= this.getTower(entity);
        entity.tower = tower;
        tower.addEntity(entity);
        this.emit(TowerEventId.EntityAdd, tower.watchers, [entity]);
    }

    /**
     * 移除实体
     * @param entity 
     */
    delEntity(entity: MapEntity) {
        let tower = entity.tower;
        if (tower) {
            entity.tower = null;
            tower.delEntity(entity);
            this.emit(TowerEventId.EntityDel, tower.watchers, [entity]);
        }
    }

    /**
     * 更新实体
     * @param entity 
     */
    updateEntity(entity: MapEntity) {
        let tower = this.getTower(entity);
        if (tower === entity.tower) {
            this.emit(TowerEventId.EntityUpdate, tower.watchers, [entity]);
            return;
        }
        if (entity.tower) {
            let delWatchers: MapEntity[] = [];
            for (let watcher of entity.tower.watchers) {
                if (!tower.watchers.includes(watcher)) {
                    delWatchers.push(watcher);
                }
            }
            entity.tower.delEntity(entity);
            let addWatchers = [];
            for (let watcher of entity.tower.watchers) {
                if (!tower.watchers.includes(watcher)) {
                    addWatchers.push(watcher);
                }
            }
            entity.tower = tower;
            tower.addEntity(entity);
            delWatchers.length && this.emit(TowerEventId.EntityDel, delWatchers, [entity]);
            addWatchers.length && this.emit(TowerEventId.EntityAdd, addWatchers, [entity]);
        } else {
            this.addEntity(entity);
        }
    }

    /**
     * 添加观察者
     * @param watcher 
     */
    addWatcher(watcher: MapEntity) {
        this.delWatcher(watcher);
        watcher.watchTowers = this.getTowers(watcher, watcher.watchWidth, watcher.watchHeight);
        let entities: MapEntity[] = [];
        for (let tower of watcher.watchTowers) {
            tower.addWatcher(watcher);
            entities = entities.concat(tower.entities);
        }
        this.emit(TowerEventId.EntityAdd, [watcher], entities);
    }

    /**
     * 移除观察者
     * @param watcher 
     */
    delWatcher(watcher: MapEntity) {
        let towers = watcher.watchTowers;
        if (towers) {
            watcher.watchTowers = null;
            let entities: MapEntity[] = [];
            for (let tower of towers) {
                tower.delWatcher(watcher);
                entities = entities.concat(tower.entities);
            }
            this.emit(TowerEventId.EntityDel, [watcher], entities);
        }
    }

    /**
     * 更新观察者
     */
    updateWatcher(watcher: MapEntity) {
        let towers = this.getTowers(watcher, watcher.watchWidth, watcher.watchHeight);
        if (watcher.watchTowers) {
            let delEntities: MapEntity[] = [];
            for (let tower of watcher.watchTowers) {
                if (!towers.includes(tower)) {
                    tower.delWatcher(watcher);
                    delEntities = delEntities.concat(tower.entities);
                }
            }
            let addEntities: MapEntity[] = [];
            for (let tower of towers) {
                if (!watcher.watchTowers.includes(tower)) {
                    tower.addWatcher(watcher);
                    addEntities = addEntities.concat(tower.entities);
                }
            }
            watcher.watchTowers = towers;
            delEntities.length && this.emit(TowerEventId.EntityDel, [watcher], delEntities);
            addEntities.length && this.emit(TowerEventId.EntityAdd, [watcher], addEntities);
        } else {
            let entities: MapEntity[] = [];
            watcher.watchTowers = towers;
            for (let tower of towers) {
                tower.addWatcher(watcher);
                entities = entities.concat(tower.entities);
            }
            entities.length && this.emit(TowerEventId.EntityAdd, [watcher], entities);
        }
    }

    private transPos(mapPos: IPos, out = tmpPos) {
        out.x = Math.max(0, Math.min(this._towerX - 1, Math.floor(mapPos.x / this._towerWidth)));
        out.y = Math.max(0, Math.min(this._towerY - 1, Math.floor(mapPos.y / this._towerHeight)));
    }

    private getTower(mapPos: IPos) {
        this.transPos(mapPos);
        return this._towers[tmpPos.y][tmpPos.x];
    }

    private getTowers(mapPos: IPos, watchWidth: number, watchHeight: number) {
        tmpPos.x = mapPos.x - watchWidth;
        tmpPos.y = mapPos.y - watchHeight;
        this.transPos(tmpPos);
        tmpPos2.x = mapPos.x + watchWidth;
        tmpPos2.y = mapPos.y + watchHeight;
        this.transPos(tmpPos2, tmpPos2);
        let towers: Tower[] = [];
        for (let y = tmpPos.y; y <= tmpPos2.y; ++y) {
            for (let x = tmpPos.x; x <= tmpPos2.x; ++x) {
                towers.push(this._towers[y][x]);
            }
        }
        return towers;
    }

}

const tmpPos: IPos = { x: 0, y: 0 };
const tmpPos2: IPos = { x: 0, y: 0 };

/**
 * 灯塔
 */
export class Tower {

    /** 所有实体 */
    entities: MapEntity[] = [];
    /** 所有观察者 */
    watchers: MapEntity[] = [];

    /**
     * 添加实体
     * @param entity 
     */
    addEntity(entity: MapEntity) {
        this.entities.push(entity);
    }

    /**
     * 移除实体
     * @param entity 
     */
    delEntity(entity: MapEntity) {
        this.entities.splice(this.entities.indexOf(entity), 1);
    }

    /**
     * 添加观察者
     * @param watcher 
     */
    addWatcher(watcher: MapEntity) {
        this.watchers.push(watcher);
    }

    /**
     * 移除观察者
     * @param watcher 
     */
    delWatcher(watcher: MapEntity) {
        this.watchers.splice(this.watchers.indexOf(watcher), 1);
    }

}

/**
 * 灯塔事件
 */
export enum TowerEventId {
    /** 实体添加 */
    EntityAdd = "EntityAdd",
    /** 实体移除 */
    EntityDel = "EntityDel",
    /** 实体更新 */
    EntityUpdate = "EntityUpdate"
}